/**
 * ...
 * >author		João Vítor de Souza
 */

package com.cupcakeengine 
{
	import com.cupcakeengine.core.BitmapRenderer;
	import com.cupcakeengine.core.IUpdatable;
	import com.cupcakeengine.core.LoopManager;
	import com.cupcakeengine.core.ObjectManager;
	import com.cupcakeengine.core.ScreenManager;
	import com.cupcakeengine.data.SharedObjectPersist;
	import com.cupcakeengine.display.AbstractScreen;
	import com.cupcakeengine.display.BitmapObject;	
	import com.cupcakeengine.display.GameObject;	
	import com.cupcakeengine.events.EventBroadcaster;
	import com.cupcakeengine.ui.KeyManager;
	import com.cupcakeengine.ui.MouseManager;
	import com.cupcakeengine.utils.Profiler;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.display.StageQuality;

	public class CKEngine 
	{
		// ___________________________________________________________________ CONSTANTS	
		
		// ___________________________________________________________________ CLASS PROPERTIES
		
		static private var _root						: Sprite;
		static private var _renderer					: BitmapRenderer;
		static private var _screenManager				: ScreenManager;
		static private var _keyManager					: KeyManager;
		static private var _mouseManager				: MouseManager;
		static private var _loopManager					: LoopManager;
		static private var _objectManager				: ObjectManager;
		static private var _sharedObjectPersist			: SharedObjectPersist;
		
		// ___________________________________________________________________ INSTANCE PROPERTIES
		
		// ___________________________________________________________________ GETTERS AND SETTERS
		
		static public function get root():Sprite { return _root; }		
		
		static public function get numObjects():int { return _renderer.numObjects; }		
		
		// ___________________________________________________________________ CONSTRUCTOR
		
		public function CKEngine()
		{
			throw new Error("CKEngine is a static class. Cannot use constructor!");
		}
		
		// ___________________________________________________________________ PUBLIC METHODS
		
		/**
		 * Initializes all the classes usded by the engine.
		 * @param	root	the root from the player used for all classes
		 * @param	id	the id to use to save data for the application
		 */
		static public function init(root:Sprite, id:String, debug:Boolean = true, useKeyboard:Boolean = true):void
		{
			_root = root;
			//_root.stage.quality = StageQuality.LOW;
			
			EventBroadcaster.init();
			
			_loopManager = LoopManager.getInstance();
			_loopManager.start(_root);
			
			_renderer = BitmapRenderer.getInstance();
			_renderer.init(_root, _root.stage.stageWidth, _root.stage.stageHeight);			
			
			_screenManager = ScreenManager.getInstance();
			_screenManager.init(_root);
			
			_keyManager = KeyManager.getInstance();
			if (useKeyboard) _keyManager.init(_root.stage);			
			
			_mouseManager = MouseManager.getInstance();
			_mouseManager.init(_root.stage);
			
			_sharedObjectPersist = SharedObjectPersist.getInstance();
			_sharedObjectPersist.init(id);	
			
			_objectManager = ObjectManager.getInstance();
			
			if (debug) new Profiler(root.stage);
		}
		
		/**
		 * Adds a BitmapObject to be rendered by the BitmapRenderer class.
		 * @param	bitmapObject	the BitmapObject to be added
		 */
		static public function addToRenderer(bitmapObject:BitmapObject):void
		{
			_renderer.add(bitmapObject);
		}
		
		/**
		 * Removes a BitmapObject from the BitmapRenderer class.
		 * @param	bitmapObject	the BitmapObject to be removed
		 */
		static public function removeFromRenderer(bitmapObject:BitmapObject):void
		{
			_renderer.remove(bitmapObject);
		}
		
		/**		 
		 * Adds a screen to the screen manager.
		 * @param	screen	the screen to be added		 
		 */		
		static public function addScreen(screen:AbstractScreen):void
		{
			_screenManager.add(screen);			
		}
		
		/**
		 * Removes a screen by its id.
		 * @param	id	the screen id
		 */
		static public function removeScreen(id:String):void
		{
			_screenManager.remove(id);			
		}
		
		/**
		 * Changes the screen.		 
		 * @param	screen	the screen to be changed if id is null
		 * @param	destroyCurrentScreen	used to keep or not more than one screen
		 */
		static public function changeToScreen(id:String, destroyCurrentScreen:Boolean = true, buildAndChange:Boolean = true):void
		{
			_screenManager.changeTo(id, destroyCurrentScreen, buildAndChange);
		}
		
		/**
		 * Returns a screen by its id.
		 * @param	id	the screen id
		 * @return	the screen at the specified id
		 */
		static public function getScreenById(id:String):AbstractScreen
		{
			return _screenManager.getScreenById(id);
		}
		
		/**
		 * Adds an object to the updated by the LoopManager class.
		 * @param	update	the object to be updated
		 * @param	id the group id the object is going to be
		 */
		static public function addUpdate(update:IUpdatable, id:String = "default"):void
		{
			_loopManager.add(update, id);
		}
		
		/**
		 * Removes an object from the LoopManager class.
		 * @param	update	the object to be removed
		 */
		static public function removeUpdate(update:IUpdatable, id:String = "default"):void
		{
			_loopManager.remove(update, id);
		}
		
		/**
		 * Starts the LoopManager class.
		 * Adds an ENTER_FRAME event to update all the objects.		 
		 */
		static public function startLoopManager():void
		{
			_loopManager.start(_root);
		}
		
		/**
		 * Stops the LoopManager class.
		 * Removes the ENTER_FRAME event to stop updating all the objects.
		 */
		static public function stopLoopManager():void
		{
			_loopManager.stop();
		}
		
		/**
		 * Pause a group of objects in the LoopManager.
		 * @param	id	id of the group of objects
		 */
		static public function pauseUpdate(id:String = "default"):void
		{
			_loopManager.pause(id);
		}
		
		/**
		 * Resume a group of objects in the LoopManager.
		 * @param	id id of the group of objects
		 */
		static public function resumeUpdate(id:String = "default"):void
		{
			_loopManager.resume(id);
		}
		
		/**
		 * Returns if a key is down.
		 * @param	keyCode	the key code
		 * @param	releaseKey	if setted to true it will check if the user released the key
		 * @return	the state of the key
		 */
		static public function isKeyDown(keyCode:uint, releaseKey:Boolean = false):Boolean 
		{
			return _keyManager.isDown(keyCode, releaseKey);
		}		
		
		/**
		 * Returns a GameObject.
		 * @param	display	the display object		 
		 * @param	bitmapDataObject	BitmapData information: width:int, height:int, transparent:Boolean = true, fillColor:uint = 4294967295
		 * @param	bitmapObject	Bitmap information: pixelSnapping:String = "auto", smoothing:Boolean = false	
		 * @return	the GameObject
		 */
		static public function getGameObject(display:DisplayObject, registerPoint:String = "TL", isTransparent:Boolean = true, bitmapDataObject:Object = null, bitmapObject:Object = null):GameObject
		{
			return _objectManager.getGameObject(display, registerPoint, isTransparent, bitmapDataObject, bitmapObject);
		}
		
		/**
		 * Returns a reusable object.
		 * @param	id	the id object	
		 * @return	the reusable object
		 */
		static public function getReusable(id:String):*
		{
			return _objectManager.getReusable(id);
		}
		
		/**
		 * Ads an object to memory to be used later.
		 * @param	id	the id object
		 * @param	data	the data object
		 */
		static public function addReusable(id:String, data:*):void
		{
			_objectManager.addReusable(id, data);
		}
		
		/**
		 * Releases from use an object.
		 * @param	id	thd is object
		 * @param	data	the data object
		 */
		static public function releaseReusable(id:String, data:*):void
		{
			_objectManager.releaseReusable(id, data);
		}
		
		// ___________________________________________________________________ PRIVATE METHODS
		
		// ___________________________________________________________________ EVENTS
	}
}

